package ubc.master;

import ubc.CrawlResult;
import ubc.ObjectReader;
import java.io.BufferedWriter;
import java.io.EOFException;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import ubc.CrawlWrapper;




public class Aggregate {

        long timeRan;
	PrintWriter out;
	CrawlResult currentResult;
	Boolean blue = true; //to colour the table
	private int numNodesCrawled = 0;

        private int numWorkers;

	//number of nodes of type x variables
	private int crawledSuccesfully = 0;
	private int otherexception = 0;
	private int timeout = 0;
	private int connectException = 0;
	private int ioException = 0;
	private int sendException = 0;
	private int noResponse = 0;
	private int notCrawled = 0;	
	private ArrayList<String> allNodes = new ArrayList<String>();

	// min/max/total number of files variables
	private int maxNumFiles = 0;
	private int minNumFiles = 0;//will always be zero...
	private long totalNumFiles = 0;
	
	//min/max/total number of files's Sizes variables
	private boolean first = true;
	private int maxFileSize = 0;
	private int minFileSize = 0;
	private float totalFileSize = 0;


        //Top 10 Extensions variables
	private ArrayList<String> allExtensions = new ArrayList<String>();
	private ArrayList<Integer> extCount = new ArrayList<Integer>();
	int foundflag = 0;
	
	public Aggregate(String path, int mode, int workers, long time, Date startDate, Date endDate) {

		timeRan = time;
                numWorkers = workers;

		//Prepare a file reader and a writer
                ObjectReader crawlReader = new ObjectReader(path);
		try {
			out = new PrintWriter(new BufferedWriter(new FileWriter("results2.html")));
		} catch (Exception e) {
			System.err.println("PrintWriter Error: ");
			e.printStackTrace();
		}

                // Print Headers
		out.println("<html>");
		out.println("Start Date     : " + startDate.toString());
		out.println("<br>End Date   : " + endDate.toString());
		out.println("<br>Time Ran   : " + time + " seconds");
                out.println("<br>Num Workers: " + numWorkers);
		out.println("<body>");
                if (mode == CrawlWrapper.FULL_MODE)
                    printFullDataHeader();
                else
                    printMinimalDataHeader();

                // Print Data and calculate statistics
		try{
                    while(true)
                    {
                        currentResult = (CrawlResult)crawlReader.readNext();
                        numNodesCrawled++;
                        computeNumberofNodesdiscovered();
                        if ( mode == CrawlWrapper.FULL_MODE ) {
                            printFullData();
                            computeAvgMinMaxNumFiles();
                            computeAvgMinMaxFileSizes();
                            computeTop10Ext();
                        }
                        else {
                            //Print minimal mode data
                            printMinimalData();
                        }
                    }
		} catch( EOFException e ){
                        // Close the Reader
			crawlReader.close();
                        
                        // No matter the mode, print table footer and num Discovered nodes
                        printTableFooter();
                        printNumberofNodesdiscovered();

                        // If mode == full, then print additional stats
                        if(mode == CrawlWrapper.FULL_MODE) {
                            printAvgMinMaxNumFiles();
                            printAvgMinMaxFileSizes();
                            printTop10Ext();
                        }

                        out.println("</body>");
                        out.println("</html>");
                        out.close();
		}
	}
	
	//Print Minimal Data
	public void printMinimalDataHeader(){
		out.println();
		out.println("<h2>Minimal Data:<h2>");
		out.println("<table border='1' bordercolor=#FFFFFF bgcolor=#A9D0F5 style='border-collapse:collapse;font-size:13px'>");
		out.println();
		out.println(
				"<tr>" +
				"<th>Host:Port</th>" +
				"<th>Agent</th>" +
				"<th>Status</th>" +
				"</tr>");
	}
	public void printMinimalData() {
		blue = !blue;
		if(blue == true) 
			out.println("<tr bgcolor=#A9D0F5>");
		else
			out.println("<tr bgcolor=white>");
		out.println("<td>" + currentResult.getHostPort() + "</td>" +
					"<td>" + currentResult.getAgent()    + "</td>" + 
					"<td>" + currentResult.getStatusString()   + "</td>" );
		out.println("</tr>");
	}
	//Print Generic Table footer
	public void printTableFooter(){
		out.println("</table><br>");
	}
	//Print Full Data
	public void printFullDataHeader(){
		out.println("<table border='1' bordercolor=#FFFFFF bgcolor=#A9D0F5 style='border-collapse:collapse;font-size:13px'>");
		out.println();
		out.println("<h2>Full Data:</h2>");
		out.println();
		out.println(
				"<tr>" +
				"<th>Host:Port</th>" +
				"<th>Agent</th>" +
				"<th>Status</th>" +
				"<th>Number of Files</th>" +
				"<th>File</th>" +
				"</tr>");		
	}
	public void printFullData() {

		blue = !blue;
		if(blue == true) 
			out.println("<tr bgcolor=#A9D0F5>");
		else
			out.println("<tr bgcolor=white>");
                        out.println(
						"<td>" + currentResult.getHostPort() + "</td>" +
						"<td>" + currentResult.getAgent()    + "</td>" + 
						"<td>" + currentResult.getStatusString()   + "</td>" +
						"<td>" + currentResult.getFilesListSize().size() + "</td>" + 
						"<td>" +
							"<form>" + 
								"<table>" +
									"<tr>" );
//			for(int j=0; j<currentResult.getFilesList().size(); j++) {
//				out.println("<td>" + currentResult.getFilesList().get(j) + "</td>");
//			}
		out.println("</tr><tr>");
		for(int j=0; j<currentResult.getFilesListExt().size(); j++) {
			out.println("<td>" + currentResult.getFilesListExt().get(j) + "</td>");
		}
		out.println("</tr><tr>");
		for(int j=0; j<currentResult.getFilesListSize().size(); j++) {
			out.println("<td>" + currentResult.getFilesListSize().get(j) + "</td>");
		}
		out.println(
									"</tr>" +
								"</table>" +
							"</form>" +
						"</td>" +				
					"</tr>");

	}	

	//Print Number of Nodes Discovered
	public void computeNumberofNodesdiscovered(){
		
		if ( allNodes.contains(currentResult.getHostPort()) == false )
			allNodes.add(currentResult.getHostPort());
//
//		// Add unique leaves
//		String leaveResult = currentResult.getLeaves();
//		if(!leaveResult.isEmpty()) {
//			String[] leaveNodes = {leaveResult};
//			if( leaveResult.contains(",") ) {
//	        	leaveNodes = leaveResult.split(",");
//			}
//                        for(int j=0; j<leaveNodes.length; j++) {
//                                if ( allNodes.contains(leaveNodes[j]) == false )
//                                        allNodes.add(leaveNodes[j]);
//                        }
//                }
//
//		// Add unique ultrapeers
//		String ultraResult = currentResult.getUltrapeers();
//		if(!ultraResult.isEmpty()) {
//			String[] ultraNodes = {ultraResult};
//			if( ultraResult.contains(",") ) {
//	        	ultraNodes = ultraResult.split(",");
//			}
//                        for(int j=0; j<ultraNodes.length; j++) {
//                                if ( allNodes.contains(ultraNodes[j]) == false )
//                                        allNodes.add(ultraNodes[j]);
//                        }
//                }
		
		// Count number of nodes for each status
		if (currentResult.getStatus() == CrawlResult.CRAWLED){
			crawledSuccesfully++;
		}else if (currentResult.getStatus() == CrawlResult.TIMEOUT_EXCEPTION){
			timeout++;
		}else if (currentResult.getStatus() == CrawlResult.CONNECT_EXCEPTION){
			connectException ++;
		}else if (currentResult.getStatus() == CrawlResult.IO_EXCEPTION){
			ioException ++;
		}else if (currentResult.getStatus() == CrawlResult.SEND_EXCEPTION){
			sendException ++;
		}
	}
	public void printNumberofNodesdiscovered(){
		
		notCrawled = allNodes.size() - numNodesCrawled;
		
		out.println("<h2>Statistics</h2>");
		out.println("<table border='1' bordercolor=#FFFFFF bgcolor=#A9D0F5 style='border-collapse:collapse;font-size:13px'>");
		out.println("<tr> <th>Node Status</th> <th>Count</th> <th>Discovery Rate</th> </tr>");
		out.println("<tr> <td>Discovered:</td> <td>"           + allNodes.size()    + "</td> <td>" + (float)allNodes.size()/timeRan    + "</td> </tr>");
		out.println("<tr> <td>Crawled:</td> <td>"              + numNodesCrawled    + "</td> <td>" + (float)numNodesCrawled/timeRan     + "</td> </tr>");
		out.println("<tr> <td>Not Crawled:</td> <td>"          + notCrawled         + "</td> <td>" + (float)notCrawled/timeRan         + "</td> </tr>");
		out.println("<tr> <td>Timeout Exception</td> <td>"     + timeout            + "</td> <td>" + (float)timeout/timeRan            + "</td> </tr>");
		out.println("<tr> <td>Connect Exception:</td> <td>"    + connectException        + "</td> <td>" + (float)connectException/timeRan        + "</td> </tr>");
		out.println("<tr> <td>IO      Exception</td> <td>"     + ioException        + "</td> <td>" + (float)ioException/timeRan        + "</td> </tr>");
		out.println("<tr> <td>Send    Exception</td> <td>"     + sendException + "</td> <td>" + (float)sendException/timeRan + "</td> </tr>");
		out.println("<tr> <td>Crawled Succesfully:</td> <td>"  + crawledSuccesfully + "</td> <td>" + (float)crawledSuccesfully/timeRan + "</td> </tr>");
		out.println("</table>");
	}

	//Print Average, Minimum, and Maximum number of files
	public void computeAvgMinMaxNumFiles(){
		
		//sum number of files per node
		totalNumFiles += currentResult.getNumOfFiles();
		
		//get the maximum number of files on a given node
		if (maxNumFiles < currentResult.getNumOfFiles())
			maxNumFiles = currentResult.getNumOfFiles();
		//get the minimum number of files on a given node, probably zero always
		if(minNumFiles > currentResult.getNumOfFiles())
			minNumFiles = currentResult.getNumOfFiles();								
	}
	public void printAvgMinMaxNumFiles() {
		float avgNumFiles;
		if (crawledSuccesfully != 0){
                avgNumFiles = totalNumFiles / crawledSuccesfully;
		}else{
			avgNumFiles = 0;
		}
		out.println("<h3>Number of Files</h3>");
		out.println("<table border='1' bordercolor=#FFFFFF bgcolor=#A9D0F5 style='border-collapse:collapse;font-size:13px'>");
		out.println("<tr> <th>Min</th> <th>Max</th> <th>Avg</th> </tr>");
		out.println("<tr>");
		out.println("<td>" + minNumFiles + "</td>");
		out.println("<td>" + maxNumFiles + "</td>");
		out.println("<td>" + avgNumFiles + "</td>");
		out.println("</tr>");
		out.println("</table>");
	}
	

	//Print Average, Minimum, and Maximum File SIZES
	public void computeAvgMinMaxFileSizes(){
            
        for (int j=0;j<currentResult.getFilesListSize().size();j++){
                
        	if (first) minFileSize = currentResult.getFilesListSize().get(j);
        	first = false ;
        		//sum number of files per node
        	totalFileSize += currentResult.getFilesListSize().get(j);
                
                //get the maximum number of files on a given node
                if (maxFileSize < currentResult.getFilesListSize().get(j))
                        maxFileSize = currentResult.getFilesListSize().get(j);
                //get the minimum number of files on a given node, probably zero always
                if(minFileSize > currentResult.getFilesListSize().get(j))
                        minFileSize = currentResult.getFilesListSize().get(j);

        }
	}
	public void printAvgMinMaxFileSizes() {
		
				float avgFileSize = totalFileSize/totalNumFiles;
		out.println("<h3>File Sizes</h3>");
		out.println("<table border='1' bordercolor=#FFFFFF bgcolor=#A9D0F5 style='border-collapse:collapse;font-size:13px'>");
		out.println("<tr> <th>Min</th> <th>Max</th> <th>Avg</th> </tr>");
		out.println("<tr>");
		out.println("<td>" + minFileSize + "</td>");
		out.println("<td>" + maxFileSize + "</td>");
		out.println("<td>" + avgFileSize + "</td>");
		out.println("</tr>");
		out.println("</table>");
	}
	
	public void computeTop10Ext(){
			
		//loop through every filename and find if they have an extension 
		for (int j=0;j<currentResult.getFilesList().size();j++){
			if (currentResult.getFilesList().get(j).lastIndexOf('.')!= -1){//check that the file has an extension
				
				//for files with extensions, compare it against the known extensions
				//if already in list of allExtensions, add 1 to the count and sort
				for (int k = 0;k<allExtensions.size();k++){
					if(currentResult.getFilesList().get(j).substring(currentResult.getFilesList().get(j).lastIndexOf('.')).compareToIgnoreCase(allExtensions.get(k)) == 0)
					{
						extCount.set(k, extCount.get(k)+1);
						int s = k;
						if (s>0){ //Sort the lists according to count number
							while (extCount.get(s)> extCount.get(s-1)){
									allExtensions.add(s-1, allExtensions.get(s));
									allExtensions.remove(s+1);
									extCount.add(s-1,extCount.get(s));
									extCount.remove(s+1);
									s--;
									if (s == 0) break;
							}
						}
						foundflag = 1;
						break;
					}
				}
				//if not found to be a known extension, add it and initiate count
				if (foundflag == 0){
					allExtensions.add(currentResult.getFilesList().get(j).substring(currentResult.getFilesList().get(j).lastIndexOf('.')));
					extCount.add(1);
				}
				else foundflag = 0;
			}
		}		
	}
	public void printTop10Ext() {
		
		//Finally select the 10 extensions with highest count
		out.println("<h3>Top Ten Extensions</h3>");
		out.println("<table border='1' bordercolor=#FFFFFF bgcolor=#A9D0F5 style='border-collapse:collapse;font-size:13px'>");
		out.println("<tr> <th>Rank</th> <th>Extension</th> <th>Count</th> </tr>");
		for (int index=0;index<10&&index<allExtensions.size();index++){
			out.println("<tr>");
			out.println("<td>" + (index+1) + "</td>" + 
					    "<td>" + allExtensions.get(index) + "</td>" + 
					    "<td>" + extCount.get(index) + "</td>");
			out.println("</tr>");
		}
		out.println("</table>");
	}

        public static void main(String[] args) {

            int mode = ubc.Constants.MINIMAL_MODE;
//            String path = ubc.Constants.serverFilePath;
            String path = "server_results2.cr";

            for(int i=0; i<args.length; i++) {
                if (args[0].equalsIgnoreCase("-full"))
                    mode = ubc.Constants.FULL_MODE;
                else if (args[0].equalsIgnoreCase("-minimal"))
                    mode = ubc.Constants.MINIMAL_MODE;
                else
                    path = args[i];
            }

            System.out.println("Mode: " + mode);
            System.out.println("Path: " + path);
            @SuppressWarnings("unused")
			Aggregate agg = new Aggregate(path, mode, 111, 1800, new Date(), new Date());
        }
}
